﻿using GSI.Core.Data;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GSI.Core
{
	public enum Type
	{
		CPUS,
		CPUP,
		GPUCuda,
		GPUCublas,
		GPUOpenCL,
		IDW_CPU,
		IDW_Parallel,
		IDW_GPU,
		GPUMCublas
	}
	public static class CoreApp
	{
		/// <summary>
		/// 采样点数据集
		/// </summary>
		private static DataGroup simpleData = new DataGroup();
		/// <summary>
		/// 网格数据
		/// </summary>
		private static MeshData mesh = new MeshData();
		/// <summary>
		/// 初始化后的采样点
		/// </summary>
		private static List<GPoint> initedSimplePoints;
		/// <summary>
		/// 返回追踪处理器
		/// </summary>
		/// <returns></returns>
		private static Track trackHandler = new Track();
		/// <summary>
		/// 绘制区域的宽度
		/// </summary>
		private static double printWidth;
		/// <summary>
		/// 绘制区域的高度
		/// </summary>
		private static double printHeight;

		/// <summary>
		/// Bitmap 的宽度
		/// </summary>
		private static double bitmapWidth;
		/// <summary>
		/// Bitmap 的长度
		/// </summary>
		private static double bitmapHeight;

		/// <summary>
		/// 采样点半径
		/// </summary>
		private static int pointR = 6;

		private static Graphics graphics;

		public static string output = "";
		public static List<Info> Infoes { get;} = new List<Info>();

		public static double GetValue(double x, double y)
		{
			if (initedSimplePoints != null)
			{
				var s = initedSimplePoints.AsParallel().Where(p => (p.X - pointR) < x && x < (p.X + pointR) && (p.Y - pointR) < y && y < (p.Y + pointR));
				if (s.Count() > 0)
					return s.ElementAt(0).Value;
			}
			return -1;
		}

		/// <summary>
		/// 读取数据
		/// </summary>
		/// <returns></returns>
		public static bool ReadData()
		{
			simpleData.Points = DataHandler.ReadDataFromFile();

			if (simpleData.Points.Count == 0)
				return false;
			else
			{
				simpleData.Init();
				return true;
			}
		}

		/// <summary>
		/// 根据地质数据绘制等值线
		/// </summary>
		/// <param name="width"></param>
		/// <param name="height"></param>
		/// <param name="cellNum"></param>
		/// <returns></returns>
		public static Bitmap PrintImage(double width, double height, int cellNum, Type? type)
		{
			bitmapWidth = (int)width;
			bitmapHeight = (int)height;
			Bitmap bitmap = null;

			if (simpleData.Points.Count == 0)
				ReadData();
			if (simpleData.Points.Count == 0)
			{
				output += $"\n{DateTime.Now.ToString() }未读取到数据.\n";
				return bitmap;
			}


			//bitmapWidth = 1760.0;
			//bitmapHeight = 945.0;
			// 对采样点进行坐标转换，对应绘图区域
			initedSimplePoints = InitialData();
			// 初始化bitmap
			bitmap = new Bitmap((int)printWidth, (int)printHeight);
			graphics = Graphics.FromImage(bitmap);

			// 初始化网格
			mesh = InitialMesh(cellNum);
			mesh.Interpolated = false;
			output += $"\n{DateTime.Now.ToString() }网格点数目{mesh.Points.Count}\n";

			// 插值算法
			KriGing kriging;
			// info
			var info = new Info();
			info.Time = DateTime.Now;
			info.MethodType = type.Value;
			switch (type)
			{
				case Type.CPUS:
					kriging = new CPUSerial(initedSimplePoints);
					output += kriging.GetInterpolationData(mesh.Points);
					break;
				case Type.CPUP:
					kriging = new CPUParallel(initedSimplePoints);
					output += kriging.GetInterpolationData(mesh.Points);
					break;
				case Type.GPUCuda:
					kriging = new GPUCUDA(initedSimplePoints);
					output += kriging.GetInterpolationData(mesh.Points);
					break;
				case Type.GPUCublas:
					kriging = new GPUMCublas(initedSimplePoints);
					output += kriging.GetInterpolationData(mesh.Points);
					break;
				case Type.GPUOpenCL:
					kriging = new GPUOpenCL(initedSimplePoints);
					output += kriging.GetInterpolationData(mesh.Points);
					break;
				case Type.IDW_CPU:
					output += IDW.UseCpuIDW(initedSimplePoints, mesh.Points);
					break;
				case Type.IDW_Parallel:
					output += IDW.UseParalleIDW(initedSimplePoints, mesh.Points);
					break;
				case Type.IDW_GPU:
					output += IDW.UseGPUIDW(initedSimplePoints, mesh.Points);
					break;
				case Type.GPUMCublas:
					kriging = new GPUMCublas(initedSimplePoints);
					output += kriging.GetInterpolationData(mesh.Points);
					break;
				default:
					break;
			}

			Infoes.Add(info);

			mesh.Init();
			mesh.Interpolated = true;

			// 颜色填充
			FillingBitmap();
			// 追踪算法
			//trackHandler.DoTrack(mesh, simpleData.SpanValue / 20.0, simpleData);
			// 绘制等值线
			//DrawingIsoline();
			// 绘制采样点
			DrawingPoints();
			return bitmap;
		}

		public static Bitmap PrintColorBar(double width, double height)
		{
			Bitmap bitmap = new Bitmap((int)(width+1), (int)(height+1));
			Graphics g = Graphics.FromImage(bitmap);
			Bitmap ColorSpline = Resources.ColorSpline;
			var colorlinelength =  ColorSpline.Width * (messageColor[1] - messageColor[0]);
			int colorNum = (int)(height/60 + 1); //色标分x份
			double colorInterval = 1.0 / colorNum; //颜色色标变化间隔
			double colorCellHeight = (bitmap.Height*1.0 - 10.0) / colorlinelength; //色标每个小长条的高度
			Font Rulefont = new Font(FontFamily.GenericMonospace,12);
			for (int i = 0; i < colorlinelength; i++)
			{
				int ColorPositon = (int)(messageColor[0] * ColorSpline.Width + i);
				Color DrawColor = ColorSpline.GetPixel(ColorPositon, 2);
				g.FillRectangle(new SolidBrush(DrawColor), new RectangleF(0, (float)(colorCellHeight * i + 5), (float)(bitmap.Width / 6), (float)colorCellHeight));
				if (i % 25 == 0 && i+25 < colorlinelength)
				{
					int ValueHeight = (int)(mesh.MinValue + (mesh.SpanValue) / colorlinelength * i);

					g.DrawString(ValueHeight.ToString(), Rulefont, Brushes.Black, new PointF((float)(bitmap.Width / 5), (float)(colorCellHeight * i)));
				}
			}
			g.DrawString(((int)mesh.MaxValue).ToString(),
							Rulefont,
							Brushes.Black,
							new PointF((float)(bitmap.Width / 5), (float)(bitmap.Height-20)));

			return bitmap;
		}

		public static void DrawRule()
		{
			//声明画笔并设置样式
			Pen RuleLinePen = new Pen(Color.Black, 1);
			float stepX = (float)(printWidth / 4);
			//绘制中间刻度线
			float stepY = (float)(printHeight / 4);
			for (int i = 0; i < 5; i++)
			{
				//设置字体
				var Rulefont = new Font("Times New Roman", 10);

				//要标记的刻度数字
				string PositionX = ((int)(simpleData.MinX + i * simpleData.SpanX / 4)).ToString();
				//获取要绘制的刻度值
				string PositionY = ((int)(simpleData.MinY + i * simpleData.SpanY / 4)).ToString();
				//获取字体的长度和宽度
				SizeF sizefX = graphics.MeasureString(PositionX, Rulefont);

				//下X轴
				graphics.DrawLine(RuleLinePen, (float)(bitmapWidth - printWidth) / 2 + i * stepX, (float)(bitmapHeight - (bitmapHeight - printHeight) / 2), (float)(bitmapWidth - printWidth) / 2 + i * stepX, (float)(bitmapHeight - (bitmapHeight - printHeight) / 2 + 5));
				graphics.DrawString(PositionX, Rulefont, new SolidBrush(Color.Black), new PointF((float)(bitmapWidth - printWidth) / 2 + i * stepX - sizefX.Width / 2, (float)(bitmapHeight - (bitmapHeight - printHeight) / 2 + 5)));

				//上X轴
				graphics.DrawLine(RuleLinePen, (float)(bitmapWidth - printWidth) / 2 + i * stepX, (float)((bitmapHeight - printHeight) / 2), (float)(bitmapWidth - printWidth) / 2 + i * stepX, (float)((bitmapHeight - printHeight) / 2 - 5));
				graphics.DrawString(PositionX, Rulefont, new SolidBrush(Color.Black), new PointF((float)(bitmapWidth - printWidth) / 2 + i * stepX - sizefX.Width / 2, (float)((bitmapHeight - printHeight) / 2 - sizefX.Height - 5)));

				//获取字体的长度和宽度
				SizeF sizefY = graphics.MeasureString(PositionY, Rulefont);
				//左Y轴
				graphics.DrawLine(RuleLinePen, (float)((bitmapWidth - printWidth) / 2), (float)(bitmapHeight - (bitmapHeight - printHeight) / 2 - i * stepY), (float)((bitmapWidth - printWidth) / 2 - 5), (float)((bitmapHeight - (bitmapHeight - printHeight) / 2 - i * stepY)));
				graphics.DrawString(PositionY, Rulefont, new SolidBrush(Color.Black), new PointF((float)((bitmapWidth - printWidth) / 2 - sizefY.Width - 5), (float)(bitmapHeight - (bitmapHeight - printHeight) / 2 - i * stepY - sizefY.Height / 2)));

				//右Y轴
				graphics.DrawLine(RuleLinePen, (float)(printWidth + (bitmapWidth - printWidth) / 2), (float)(bitmapHeight - (bitmapHeight - printHeight) / 2 - i * stepY), (float)(printWidth + (bitmapWidth - printWidth) / 2 + 5), (float)(bitmapHeight - (bitmapHeight - printHeight) / 2 - i * stepY));
				graphics.DrawString(PositionY, Rulefont, new SolidBrush(Color.Black), new PointF((float)(printWidth + (bitmapWidth - printWidth) / 2 + 5), (float)(bitmapHeight - (bitmapHeight - printHeight) / 2 - i * stepY - sizefY.Height / 2)));

				//绘图区域边框
				graphics.DrawRectangle(RuleLinePen, (float)((bitmapWidth - printWidth) / 2 - 1), (float)((bitmapHeight - printHeight) / 2 - 1), (float)printWidth, (float)printHeight);
			}
		}

		private static void DrawingPoints()
		{
			//声明画笔并设置样式
			Pen PointPen = new Pen(Color.Black);
			//绘图采样点区域边框   四个参数分别是 起点坐标  和 宽度高度
			graphics.DrawRectangle(PointPen, 0.0f, 0.0f, (float)printWidth - 1.0f, (float)printHeight - 1.0f);
			//graphics.DrawRectangle(PointPen, (float)((bitmapWidth - printWidth) / 2), (float)((bitmapHeight - printHeight) / 2), (float)printWidth - 1.0f, (float)printHeight - 1.0f);
			//绘制采样点  已按照左下角为坐标原点
			foreach (GPoint point in initedSimplePoints)
			{
				var x = (float)(point.X);
				var y = (float)(point.Y);

				//var x = (float)(point.X + (bitmapWidth - printWidth) / 2);
				//var y = (float)(point.Y + (bitmapHeight - printHeight) / 2);
				graphics.FillEllipse(Brushes.Black, x, y, pointR, pointR);
				//graphics.DrawString(
				//	$"({(int)point.X},{(int)point.Y},{point.Value})",
				//	new Font("宋体", 12, FontStyle.Regular),
				//	 new SolidBrush(Color.Black),
				//	 x, y);
			}
		}

		/// <summary>
		/// 对采样点进行坐标转换
		/// </summary>
		/// <param name="width"></param>
		/// <param name="height"></param>
		/// <returns></returns>
		private static List<GPoint> InitialData()
		{

			// 计算绘制区域大小
			var scale = 0.0;
			if (bitmapWidth / bitmapHeight < simpleData.SpanX / simpleData.SpanY)
				scale = bitmapWidth / simpleData.SpanX;
			else
				scale = bitmapHeight / simpleData.SpanY;
			printWidth = simpleData.SpanX * scale;
			printHeight = simpleData.SpanY * scale;

			// 设立做下坐标原点，并根据原点初始化坐标
			List<GPoint> initedSimplePoints = simpleData.Points.Select(p => new GPoint {
				X = p.X,
				Y = simpleData.MaxY - p.Y,
				Value = p.Value
			}).ToList();

			var iMaxX = initedSimplePoints.Max(p=>p.X);
			var iMaxY = initedSimplePoints.Max(p=>p.Y);
			var iMinX = initedSimplePoints.Min(p=>p.X);
			var iMinY = initedSimplePoints.Min(p=>p.Y);

			// 根据绘制区域大小转换坐标
			initedSimplePoints.ForEach(p =>
			{
				p.X = (p.X - iMinX) / (iMaxX - iMinX) * printWidth;
				p.Y = (p.Y - iMinY) / (iMaxY - iMinY) * printHeight;
			});

			return initedSimplePoints;

			// 初始化网格

		}

		/// <summary>
		/// 初始化网格
		/// </summary>
		/// <param name="cellNum"></param>
		/// <returns></returns>
		private static MeshData InitialMesh(int cellNum)
		{
			var mesh = new MeshData();

			if (simpleData.SpanX > simpleData.SpanY)
			{
				mesh.CellSize = printWidth / cellNum;
				mesh.CellXNum = cellNum;
				mesh.CellYNum = (int)(printHeight / mesh.CellSize);
			}
			else
			{
				mesh.CellSize = printHeight / cellNum;
				mesh.CellYNum = cellNum;
				mesh.CellXNum = (int)(printWidth / mesh.CellSize);
			}

			var meshPoints = new List<GPoint>();

			for (int j = 0; j < mesh.CellYNum; j++)
			{
				for (int i = 0; i < mesh.CellXNum; i++)
				{
					meshPoints.Add(new GPoint {
						X = i * mesh.CellSize,
						Y = j * mesh.CellSize,
						Value = 0
					});
				}
				//添加右侧边界的一列
				meshPoints.Add(new GPoint {
					X = printWidth,
					Y = j * mesh.CellSize,
					Value = 0
				});
			}
			//添加下侧边界的一列
			for (int i = 0; i < mesh.CellXNum + 1; i++)
			{
				meshPoints.Add(new GPoint {
					X = i * mesh.CellSize,
					Y = printHeight,
					Value = 0
				});
			}

			mesh.Points = meshPoints;
			return mesh;
		}

		/// <summary>
		/// 颜色填充
		/// </summary>
		private static double[] messageColor = new double[] {0.1,0.8};
		private static void FillingBitmap()
		{
			Bitmap ColorSpline = Resources.ColorSpline;
			var meshPointArray = mesh.pointsArray;

			for (int j = 0; j < mesh.CellYNum; j++)
			{
				for (int i = 0; i < mesh.CellXNum; i++)
				{
					double averageValue = (meshPointArray[i, j].Value + meshPointArray[i + 1, j].Value + meshPointArray[i, j + 1].Value + meshPointArray[i + 1, j + 1].Value) / 4;

					int ColorPosition = (int)(messageColor[0] * ColorSpline.Width + (averageValue - mesh.MinValue) / (mesh.SpanValue) * (messageColor[1] - messageColor[0]) * ColorSpline.Width);
					if (ColorPosition < 0)
						continue;
					Color DrawColor = ColorSpline.GetPixel(ColorPosition, 2);
					graphics.FillRectangle(new SolidBrush(DrawColor), new Rectangle(
						(int)(meshPointArray[i, j].X),
						(int)(meshPointArray[i, j].Y),
						(int)(meshPointArray[i + 1, j].X - meshPointArray[i, j].X) + 1,
						(int)(meshPointArray[i, j + 1].Y - meshPointArray[i, j].Y) + 1));
					//graphics.FillRectangle(new SolidBrush(DrawColor), new Rectangle(
					//	(int)((bitmapWidth - printWidth) / 2 + meshPointArray[i, j].X),
					//	(int)((bitmapHeight - printHeight) / 2 + meshPointArray[i, j].Y),
					//	(int)(meshPointArray[i + 1, j].X - meshPointArray[i, j].X)+1,
					//	(int)(meshPointArray[i, j + 1].Y - meshPointArray[i, j].Y)+1));
				}
			}
		}

		private static void DrawingIsoline()
		{
			Pen linePen;
			int k = 0;
			foreach (var lines in trackHandler.contourData)
			{
				//设置等值线等间隔加粗
				k++;
				//LineThickInterval 为 1 的时候，即为不加粗
				if (k % 5 == 0 && 5 != 1)
				{
					linePen = new Pen(Color.Black, 2);
				}
				else
				{
					linePen = new Pen(Color.Black, 1);
				}
				double valueStart = simpleData.MinValue;
				double valueEnd = simpleData.MaxValue;
				foreach (var line in lines.Value)
				{
					if (lines.Key <= valueEnd && lines.Key >= valueStart)
						graphics.DrawLine(linePen, (float)(line.X1), (float)(line.Y1),
										(float)(line.X2), (float)(line.Y2));
					//graphics.DrawLine(linePen, (float)((bitmapWidth - printWidth) / 2 + line.X1), (float)((bitmapHeight - printHeight) / 2 + line.Y1),
					//					(float)((bitmapWidth - printWidth) / 2 + line.X2), (float)((bitmapHeight - printHeight) / 2 + line.Y2));
				}
			}
		}
	}
}


/*
 
	 Severity	Code	Description	Project	File	Line	Suppression State
Error	CS0050	Inconsistent accessibility: return type 'List<GPoint>' is less accessible than method 
'KriGing.GetInterpolationData(List<GPoint>)'	
GSI.Core	C:\Users\sha\documents\visual studio 2015\Projects\Geological Spatial Interpolation\GSI.Core\Interpolations\KriGing.cs	125	Active

*/
